import random
import os
import time
import pymysql
import pymysql.cursors
from datetime import datetime

#游戏实体
class GameEntity:
    def __init__(self, name, hp, max_hp, attack, defense):
        self.name = name
        self.hp = hp
        self.max_hp = max_hp
        self.attack = attack
        self.defense = defense

    #判断实体是否存活
    def is_alive(self):
        return self.hp > 0

    #计算实际受到的伤害，并更新当前生命值。
    def take_damage(self, damage):
        actual_damage = max(1, damage - self.defense)
        self.hp = max(0, self.hp - actual_damage)
        return actual_damage

    #攻击另一个实体
    def attack_entity(self, target):
        damage = max(1, self.attack - target.defense)
        target.take_damage(damage)
        return damage

#玩家，继承游戏实体
class Player(GameEntity):
    def __init__(self, name="勇者", hp=100, max_hp=100, attack=10, defense=5, position=(0, 0), inventory=None):
        super().__init__(name, hp, max_hp, attack, defense)
        self.inventory = inventory if inventory else []
        self.position = position

    #添加物品
    def add_item(self, item):
        self.inventory.append(item)
        if 'attack' in item:
            self.attack += item['attack']
        if 'defense' in item:
            self.defense += item['defense']

    #移除物品
    def remove_item(self, item):
        self.inventory.remove(item)
        if 'attack' in item:
            self.attack -= item['attack']
        if 'defense' in item:
            self.defense -= item['defense']

#怪物，继承游戏实体
class Monster(GameEntity):
    def __init__(self, monster_id, db_cursor):
        db_cursor.execute("select * from monsters where id = %s", (monster_id,))
        monster_data = db_cursor.fetchone()
        super().__init__(monster_data['name'], monster_data['hp'], monster_data['max_hp'], monster_data['attack'], monster_data['defense'])
        self.monster_id = monster_id
        self.symbol = monster_data['symbol']
        self.color = monster_data['color']
        self.drop_items = eval(monster_data['drop_items'])

    #获取掉落物
    def get_drop_items(self):
        dropped = []
        for item_id, chance in self.drop_items:
            if random.random() < chance:
                dropped.append(item_id)
        return dropped

#游戏物品
class Item:
    def __init__(self, item_id, db_cursor):
        db_cursor.execute("select * from items where id = %s", (item_id,))
        item_data = db_cursor.fetchone()
        self.id = item_id
        self.name = item_data['name']
        self.description = item_data['description']
        self.attack = item_data['attack']
        self.defense = item_data['defense']

    #获取游戏物品
    @staticmethod
    def get_item(item_id, db_cursor):
        db_cursor.execute("select * from items where id = %s", (item_id,))
        item_data = db_cursor.fetchone()
        if item_data:
            return {
                'id': item_id,
                'name': item_data['name'],
                'description': item_data['description'],
                'attack': item_data['attack'],
                'defense': item_data['defense']
            }
        return None

class Map:
    def __init__(self, width=15, height=15):
        self.width = width
        self.height = height
        #地图信息
        self.grid = [[None for _ in range(width)] for _ in range(height)]
        self.monsters = {}

    #生成地图
    def generate_map(self, player, db_cursor, monster_count=10):
        self.place_entity(player.position[0], player.position[1], 'player')
        
        db_cursor.execute("select id from monsters")
        monster_ids = [row['id'] for row in db_cursor.fetchall()]
        for i in range(monster_count):
            x, y = self.find_empty_position()
            monster_id = random.choice(monster_ids)
            monster = Monster(monster_id, db_cursor)
            self.place_entity(x, y, monster)
            self.monsters[(x, y)] = monster

    #寻找空位置
    def find_empty_position(self):
        while True:
            x = random.randint(0, self.width - 1)
            y = random.randint(0, self.height - 1)
            if self.grid[y][x] is None:
                return x, y

    #放置实体
    def place_entity(self, x, y, entity):
        if 0 <= x < self.width and 0 <= y < self.height:
            self.grid[y][x] = entity

    #移动实体
    def move_entity(self, from_x, from_y, to_x, to_y):
        if 0 <= to_x < self.width and 0 <= to_y < self.height:
            if self.grid[to_y][to_x] is None:
                self.grid[to_y][to_x] = self.grid[from_y][from_x]
                self.grid[from_y][from_x] = None
                if (from_x, from_y) in self.monsters:
                    self.monsters[(to_x, to_y)] = self.monsters.pop((from_x, from_y))
                return True
        return False

    #获取实体信息
    def get_entity(self, x, y):
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.grid[y][x]
        return None

    #移除实体
    def remove_entity(self, x, y):
        if 0 <= x < self.width and 0 <= y < self.height:
            self.grid[y][x] = None
            if (x, y) in self.monsters:
                del self.monsters[(x, y)]


#日志类
class Logger:
    def __init__(self, db_cursor, user_id):
        self.logs = []
        self.db_cursor = db_cursor
        self.user_id = user_id

    #添加日志
    def log(self, message):
        self.logs.append(message)
        if len(self.logs) > 50:
            self.logs.pop(0)
        self.print_logs()
        #self.save_log_to_db(message)

    #输出日志
    def print_logs(self):
        os.system('cls' if os.name == 'nt' else 'clear')
        print("\n" + "=" * 40)
        print("日志:")
        print("-" * 40)
        for log in self.logs[-10:]:
            print(log)
        print("-" * 40)

    #保存日志到数据库中
    def save_log_to_db(self, message):
        sql = "insert into logs (user_id, log_message) values (%s, %s)"
        self.db_cursor.execute(sql, (self.user_id, message))
        self.db_cursor.connection.commit()

#游戏控制类
class Game:
    COLORS = {
        'normal': '\033[0m',
        'red': '\033[91m',
        'green': '\033[92m',
        'yellow': '\033[93m',
        'blue': '\033[94m',
        'purple': '\033[95m'
    }

    def __init__(self):
        # 数据库连接配置
        self.db_config = {
            'host': 'localhost',
            'user': 'root',
            'password': '123456',
            'database': 'rpggame',
            'charset': 'utf8mb4',
            'cursorclass': pymysql.cursors.DictCursor
        }
        self.conn = None
        self.cursor = None
        self.player = None
        self.map = Map()
        self.logger = None
        self.running = True
        self.user_id = None
        self.connect_db()

    #连接数据库
    def connect_db(self):
        try:
            self.conn = pymysql.connect(**self.db_config)
            self.cursor = self.conn.cursor()
        except pymysql.Error as e:
            print(f"数据库连接失败: {e}")
            exit(1)

    #登录
    def login(self):
        while True:
            username = input("请输入用户名: ")
            password = input("请输入密码: ")
            
            with self.conn.cursor() as cursor:
                # 检查用户
                sql = "select * from users where username = %s and password = %s"
                cursor.execute(sql, (username, password))
                user = cursor.fetchone()
                
                if user:
                    self.user_id = user['id']
                    # 获取玩家状态
                    sql = "select * from players where user_id = %s"
                    cursor.execute(sql, (user['id'],))
                    player_data = cursor.fetchone()
                    
                    if player_data:
                        # 更新最后登录时间
                        sql = "update players set last_login = now() where id = %s"
                        cursor.execute(sql, (player_data['id'],))
                        self.conn.commit()
                        
                        # 从数据库恢复玩家状态
                        inventory = self.load_inventory()
                        self.player = Player(
                            name=username,
                            hp=player_data['hp'],
                            max_hp=player_data['max_hp'],
                            attack=player_data['attack'],
                            defense=player_data['defense'],
                            position=(player_data['position_x'], player_data['position_y']),
                            inventory=inventory
                        )
                        self.logger = Logger(self.cursor, self.user_id)
                        self.logger.log(f"欢迎回来，{username}!")
                    else:
                        # 新玩家，创建初始状态
                        sql = "insert into players (user_id) values (%s)"
                        cursor.execute(sql, (user['id'],))
                        self.conn.commit()
                        
                        self.player = Player(name=username)
                        self.logger = Logger(self.cursor, self.user_id)
                        self.logger.log(f"欢迎新玩家，{username}!")
                    break
                else:
                    register = input("用户名或密码错误，是否注册新用户? (y/n): ").lower()
                    if register == 'y':
                        self.register(username, password)
                        self.user_id = self.get_user_id(username)
                        self.player = Player(name=username)
                        self.logger = Logger(self.cursor, self.user_id)
                        self.logger.log(f"注册成功，欢迎 {username}!")
                        break

    #注册
    def register(self, username, password):
        try:
            with self.conn.cursor() as cursor:
                # 插入新用户
                sql = "insert into users (username, password) values (%s, %s)"
                cursor.execute(sql, (username, password))
                
                # 创建玩家记录
                user_id = cursor.lastrowid
                sql = "insert into players (user_id) values (%s)"
                cursor.execute(sql, (user_id,))
                
            self.conn.commit()
        except pymysql.Error as e:
            print(f"注册失败: {e}")

    #获取用户id
    def get_user_id(self, username):
        with self.conn.cursor() as cursor:
            sql = "select id from users where username = %s"
            cursor.execute(sql, (username,))
            user = cursor.fetchone()
            return user['id']

    #保存玩家个人信息
    def save_player_state(self):
        if not self.player:
            return
            
        with self.conn.cursor() as cursor:
            # 获取用户ID
            sql = "select id from users where username = %s"
            cursor.execute(sql, (self.player.name,))
            user = cursor.fetchone()
            
            if user:
                sql = """
                    update players 
                    set hp = %s, max_hp = %s, attack = %s, defense = %s,
                        position_x = %s, position_y = %s,
                        last_login = now()
                    where user_id = %s
                """
                cursor.execute(sql, (
                    self.player.hp, self.player.max_hp, 
                    self.player.attack, self.player.defense,
                    self.player.position[0], self.player.position[1],
                    user['id']
                ))
                self.save_inventory()
                self.conn.commit()

    #加载玩家背包物品信息
    def load_inventory(self):
        inventory = []
        with self.conn.cursor() as cursor:
            sql = "select item_id, quantity from player_inventory where user_id = %s"
            cursor.execute(sql, (self.user_id,))
            for row in cursor.fetchall():
                item = Item.get_item(row['item_id'], cursor)
                for _ in range(row['quantity']):
                    inventory.append(item)
        return inventory

    #保存玩家背包物品信息
    def save_inventory(self):
        with self.conn.cursor() as cursor:
            # 清空现有背包记录
            sql = "delete from player_inventory where user_id = %s"
            cursor.execute(sql, (self.user_id,))
            
            # 统计物品数量
            item_counts = {}
            for item in self.player.inventory:
                item_id = item['id']
                if item_id in item_counts:
                    item_counts[item_id] += 1
                else:
                    item_counts[item_id] = 1
            
            # 插入新的背包记录
            for item_id, quantity in item_counts.items():
                sql = "insert into player_inventory (user_id, item_id, quantity) values (%s, %s, %s)"
                cursor.execute(sql, (self.user_id, item_id, quantity))
            self.conn.commit()

    #启动游戏
    def start(self):
        self.login()
        self.map.generate_map(self.player, db_cursor=self.cursor)
        self.logger.log("欢迎来到文字RPG游戏!")
        self.logger.log("使用 w(上), a(左), s(下), d(右) 移动角色")
        self.logger.log("探索地图，击败怪物，收集物品")
        self.logger.log("输入 '使用道具/u' 来使用背包中的道具")
        self.display_status()
        self.main_loop()

    #玩家操作
    def main_loop(self):
        while self.running:
            self.render_map()
            self.display_status()
            command = input("输入命令 (w/a/s/d/查看背包/i/使用道具/u/退出/q): ").lower()

            # 获取玩家状态

            sql = "select * from players where user_id = %s"
            self.cursor.execute(sql, (self.user_id,))
            player_data = self.cursor.fetchone()

            if player_data:
                # 从数据库恢复玩家状态
                inventory = self.load_inventory()
                self.player = Player(
                    name=self.player.name,
                    hp=player_data['hp'],
                    max_hp=player_data['max_hp'],
                    attack=player_data['attack'],
                    defense=player_data['defense'],
                    position=(player_data['position_x'], player_data['position_y']),
                    inventory=inventory
                )

            if command == 'w':
                self.move_player(0, -1)
            elif command == 's':
                self.move_player(0, 1)
            elif command == 'a':
                self.move_player(-1, 0)
            elif command == 'd':
                self.move_player(1, 0)
            elif command == 'i' or command == '查看背包':
                self.show_inventory()
            elif command == 'u' or command == '使用道具':
                self.use_item()
            elif command == 'q' or command == '退出':
                self.save_player_state()
                self.running = False
                self.logger.log("感谢游玩!")
            else:
                self.logger.log("无效命令，请重试")

            self.save_player_state()

    #玩家移动
    def move_player(self, dx, dy):
        x, y = self.player.position
        new_x = x + dx
        new_y = y + dy

        if 0 <= new_x < self.map.width and 0 <= new_y < self.map.height:
            entity = self.map.get_entity(new_x, new_y)
            
            if entity is None:
                self.map.move_entity(x, y, new_x, new_y)
                self.player.position = (new_x, new_y)
                self.logger.log(f"移动到位置 ({new_x}, {new_y})")
                self.logger.save_log_to_db(f"移动到位置 ({new_x}, {new_y})")
            elif entity == 'player':
                self.logger.log("你已经在这里了")
            else:  # 遇到怪物
                self.logger.log(f"你遇到了 {entity.name}!")
                self.start_combat(entity, new_x, new_y)
        else:
            self.logger.log("无法移动：边界限制")

    #战斗控制
    def start_combat(self, monster, x, y):
        while monster.is_alive() and self.player.is_alive():
            # 玩家回合
            damage = self.player.attack_entity(monster)
            self.logger.log(f"你攻击了 {monster.name}，造成 {damage} 点伤害！")
            self.logger.save_log_to_db(f"你攻击了 {monster.name}，造成 {damage} 点伤害！")
            
            if not monster.is_alive():
                self.logger.log(f"你击败了 {monster.name}！")
                self.logger.save_log_to_db(f"你击败了 {monster.name}！")
                self.map.remove_entity(x, y)
                self.collect_loot(monster)
                break

            # 怪物回合
            damage = monster.attack_entity(self.player)
            self.logger.log(f"{monster.name} 攻击了你，造成 {damage} 点伤害！")
            self.logger.save_log_to_db(f"{monster.name} 攻击了你，造成 {damage} 点伤害！")
            
            if not self.player.is_alive():
                self.logger.log("你被击败了！游戏结束。")
                self.logger.save_log_to_db("你被击败了！游戏结束。")
                self.running = False
                break

    #收集掉落物
    def collect_loot(self, monster):
        drops = monster.get_drop_items()
        if drops:
            for item_id in drops:
                item = Item.get_item(item_id, self.cursor)
                if item:
                    self.player.add_item(item)
                    self.logger.log(f"获得物品：{item['name']}")
                    self.logger.save_log_to_db(f"获得物品：{item['name']}")
        else:
            self.logger.log(f"{monster.name} 没有掉落任何物品")

    #输出地图
    def render_map(self):
        print("\n" + "=" * 40)
        print("地图:")
        for y in range(self.map.height):
            row = ""
            for x in range(self.map.width):
                entity = self.map.get_entity(x, y)
                if entity is None:
                    row += ". "
                elif entity == 'player':
                    row += f"{self.COLORS['blue']}@{self.COLORS['normal']} "
                else:
                    row += f"{self.COLORS[entity.color]}{entity.symbol}{self.COLORS['normal']} "
            print(row)
        print("=" * 40)

    #输出玩家信息
    def display_status(self):
        print("\n" + "=" * 40)
        print(f"角色: {self.player.name}")
        print(f"位置: ({self.player.position[0]}, {self.player.position[1]})")
        print(f"生命: {self.player.hp}/{self.player.max_hp}")
        print(f"攻击: {self.player.attack}")
        print(f"防御: {self.player.defense}")
        print("=" * 40)

    #输出背包信息
    def show_inventory(self):
        os.system('cls' if os.name == 'nt' else 'clear')
        print("\n" + "=" * 40)
        print("背包:")
        print("-" * 40)
        
        if not self.player.inventory:
            print("你的背包是空的")
        else:
            # 分组统计物品
            item_counts = {}
            for item in self.player.inventory:
                name = item['name']
                if name in item_counts:
                    item_counts[name] += 1
                else:
                    item_counts[name] = 1
            
            for name, count in item_counts.items():
                item = next((i for i in self.player.inventory if i['name'] == name), None)
                if item:
                    stats = ""
                    if 'attack' in item:
                        stats += f" +{item['attack']}攻击"
                    if 'defense' in item:
                        stats += f" +{item['defense']}防御"
                    print(f"{name} x{count}{stats}")
                    print(f"  {item['description']}")
        
        print("-" * 40)
        input("按任意键继续...")

    #使用物品控制
    def use_item(self):
        if not self.player.inventory:
            self.logger.log("你的背包是空的，没有道具可以使用。")
            return

        os.system('cls' if os.name == 'nt' else 'clear')
        print("\n" + "=" * 40)
        print("可使用的道具:")
        print("-" * 40)

        for i, item in enumerate(self.player.inventory, start=1):
            stats = ""
            if 'attack' in item:
                stats += f" +{item['attack']}攻击"
            if 'defense' in item:
                stats += f" +{item['defense']}防御"
            print(f"{i}. {item['name']}{stats}")
            print(f"  {item['description']}")

        print("-" * 40)
        try:
            choice = int(input("输入要使用的道具编号 (输入 0 取消): "))
            if choice == 0:
                return
            elif 1 <= choice <= len(self.player.inventory):
                item = self.player.inventory[choice - 1]
                self.player.remove_item(item)
                self.logger.log(f"你使用了 {item['name']}。")
            else:
                self.logger.log("无效的选择，请重试。")
        except ValueError:
            self.logger.log("输入无效，请输入一个数字。")

if __name__ == "__main__":
    game = Game()
    game.start()
